home *** CD-ROM | disk | FTP | other *** search
/ Super PC 34 / Super PC 34 (Shareware).iso / spc / UTIL / DJGPP2 / V2 / DJLSR200.ZIP / src / libc / compat / mntent / mntent.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-04  |  25.0 KB  |  772 lines

  1. /* Copyright (C) 1996 DJ Delorie, see COPYING.DJ for details */
  2. /* Copyright (C) 1995 DJ Delorie, see COPYING.DJ for details */
  3. /*
  4.  * This is implementation of getmntent() and friends for DJGPP v2.x.
  5.  *
  6.  * Copyright (c) 1995-96 Eli Zaretskii <eliz@is.elta.co.il>
  7.  *
  8.  * This software may be used freely so long as this copyright notice is
  9.  * left intact.  There is no warranty on this software.
  10.  *
  11.  * ---------------------------------------------------------------------
  12.  *
  13.  * The primary motivation for these functions was the GNU df program,
  14.  * which lists all the mounted filesystems with a summary of the disk
  15.  * space available on each one of them.  However, they are also useful
  16.  * on their own right.
  17.  *
  18.  * Unlike Unix, where all mountable filesystems can be found on special
  19.  * file (and thus implementing these function boils down to reading that
  20.  * file), with MS-DOS it's a mess.  Every type of drive has its own
  21.  * interface; there are JOINed and SUBSTed pseudo-drives and RAM disks;
  22.  * different network redirectors hook DOS in a plethora of incompatible
  23.  * ways; a single drive A: can be mapped to either A: or B:, etc.  That
  24.  * is why this implementation uses almost every trick in the book to get
  25.  * at the intimate details of every drive.  Some places where you might
  26.  * find these tricks are: ``Undocumented DOS, 2nd ed.'' by Schulman et al
  27.  * and Ralf Brown's Interrupt List.
  28.  *
  29.  */
  30. #include <libc/stubs.h>
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <ctype.h>
  34. #include <errno.h>
  35. #include <sys/stat.h>
  36. #include <mntent.h>
  37. #include <dir.h>
  38. #include <dos.h>
  39. #include <bios.h>
  40. #include <dpmi.h>
  41. #include <go32.h>
  42. #include <libc/farptrgs.h>
  43. #include <sys/movedata.h>
  44. #include <libc/unconst.h>
  45.  
  46. /* Macro to convert a segment and an offset to a "far offset" suitable
  47.    for _farxxx() functions of DJGPP.  */
  48. #ifndef MK_FOFF
  49. #define MK_FOFF(s,o) ((int)((((unsigned long)(s)) << 4) + (unsigned short)(o)))
  50. #endif
  51.  
  52. #define CDS_JOIN     0x2000
  53. #define CDS_VALID    0xc000
  54. #define REMOVABLE    0
  55. #define FIXED        1
  56.  
  57. /* Static variables.  */
  58.  
  59. static char          drive_number = -1;
  60. static char          skip_drive_b = 0;
  61. static char          drive_a_mapping = 0;
  62. static char          cds_drives   = 0;
  63. static unsigned long cds_address;
  64. static int           cds_elsize;
  65. static unsigned short dos_mem_base, our_mem_base;
  66. static struct mntent mntent;
  67. static unsigned char drive_string[128];
  68. static char          *mnt_type;
  69. static unsigned char mnt_dir[128];
  70. static unsigned char mnt_fsname[128];
  71. static          char dev_opts[] = "r ,dev=  ";
  72.  
  73. static char NAME_dblsp[] = "dblsp";
  74. static char NAME_stac[] = "stac";
  75. static char NAME_ram[] = "ram";
  76. static char NAME_cdrom[] = "cdrom";
  77. static char NAME_net[] = "net";
  78. static char NAME_fd[] = "fd";
  79. static char NAME_hd[] = "hd";
  80. static char NAME_subst[] = "subst";
  81. static char NAME_join[] = "join";
  82.  
  83. int _is_remote_drive(int);
  84.  
  85. /* Static helper functions.  */
  86.  
  87. /*
  88.  * Get the entry for this disk in the DOS Current Directory Structure
  89.  * (CDS).  In case of success, return this drive's attribute word; or
  90.  * 0 in case of failure.  Fill the buffer at CURRDIR with the current
  91.  * directory on that drive.
  92.  * The pointer to the CDS array and the size of the array element
  93.  * (which are DOS version-dependent) are computed when setmntent() is
  94.  * called.
  95.  */
  96. static int
  97. get_cds_entry(int drive_num, char *currdir)
  98. {
  99.   unsigned long  cds_entry_address;
  100.   if (!cds_address)
  101.     {
  102.       *currdir = '\0';
  103.       return 0;
  104.     }
  105.  
  106.   /* The address of the CDS element for this drive.  */
  107.   cds_entry_address = cds_address + (drive_num - 1)*cds_elsize;
  108.   
  109.   /* The current directory: 67-byte ASCIIZ string at the beginning
  110.      of the CDS structure for our drive.  */
  111.   movedata(dos_mem_base, (cds_entry_address & 0xfffff),
  112.            our_mem_base, (unsigned int)currdir, 0x43);
  113.  
  114.   /* The drive attribute word is at the offset 43h, right after the
  115.      current directory string.  */
  116.   return _farpeekw(dos_mem_base, cds_entry_address + 0x43);
  117. }
  118.  
  119. /*
  120.  * For a PC with a single floppy drive, that drive can be referenced
  121.  * as both A: and B:.  This function returns the logical drive number
  122.  * which was last used to reference a physical drive, or 0 if the
  123.  * drive has only one logical drive assigned to it (which means there
  124.  * are two floppies in this system).
  125.  */
  126. static int
  127. assigned_to(int drive_num)
  128. {
  129.   __dpmi_regs r;
  130.  
  131.   /* Issue Int 21h/AX=440Eh to get logical drive last used to
  132.      reference the physical drive DRIVE_NUM.  */
  133.   r.x.ax = 0x440e;
  134.   r.h.bl = drive_num;
  135.   __dpmi_int(0x21, &r);
  136.   if (r.x.flags & 1)
  137.     return 0;
  138.   return r.h.al;
  139. }
  140.  
  141. /*
  142.  * Check if the drive is compressed with DoubleSpace.  If it is,
  143.  * get the host drive on which the Compressed Volume File (CVF)
  144.  * resides, put the name of that CVF into MNT_FSNAME[] and return
  145.  * non-zero.
  146.  */
  147. static int
  148. get_doublespace_info(int drive_num)
  149. {
  150.   __dpmi_regs r;
  151.  
  152.   r.x.ax = 0x4a11;      /* DBLSPACE Int 2Fh API */
  153.   r.x.bx = 1;
  154.   r.h.dl = drive_num - 1; /* 0 = A: */
  155.   __dpmi_int(0x2f, &r);
  156.  
  157.   if (r.x.ax == 0 && r.h.bl & 0x80)
  158.     {
  159.       int host = r.h.bl & 0x7f;
  160.  
  161.       /* Compressed drive, get the host and sequence number of the CVF.  */
  162.       sprintf(mnt_fsname, "%c:\\DBLSPACE.%03u", 'A' + host, r.h.bh);
  163.       mnt_type = NAME_dblsp;
  164.       return 1;
  165.     }
  166.   else
  167.     /* Error, or uncompressed drive (might be a host, but don't care).  */
  168.     return 0;
  169. }
  170.  
  171. /*
  172.  * For a drive compressed with Stacker, get the name of the host
  173.  * of its compressed volume file, fill MNT_FSNAME[] with its
  174.  * name and return non-zero.  If this drive isn't controlled by
  175.  * Stacker, return 0.
  176.  */
  177. static int
  178. get_stacker_info(int drive_num)
  179. {
  180.   __dpmi_regs r;
  181.   unsigned long stac_driver_ptr;
  182.  
  183.   /* Put a known DWORD into the Transfer Buffer.  If this drive
  184.      isn't compressed with Stacker, it will remain unchanged.  */
  185.   _farpokel(dos_mem_base, __tb, 0xbadabadaU);
  186.  
  187.   r.x.ax = 0x4404;      /* Stacker Get Driver Address function */
  188.   r.h.bl = drive_num;
  189.   r.x.cx = 4;
  190.   r.x.ds = __tb >> 4;
  191.   r.x.dx = __tb & 15;
  192.   __dpmi_int(0x21, &r);
  193.  
  194.   if ((stac_driver_ptr = _farpeekl(dos_mem_base, __tb)) == 0xbadabadaU)
  195.     return 0;
  196.  
  197.   /* This drive MIGHT be compressed with Stacker.  Construct a linear
  198.      address of the far pointer into the Stacker device driver.  */
  199.   stac_driver_ptr = ((stac_driver_ptr >> 12) & 0xffff0)
  200.                     + (stac_driver_ptr & 0xffff);
  201.  
  202.   /* Sanity check: real-mode addresses are only 20 bit-long, so we can
  203.      safely reject anything that's larger than FFFFFh, lest we get an
  204.      illegal address abort when we try to peek at the signature below.  */
  205.   if (stac_driver_ptr > 0xfffff)
  206.     return 0;
  207.  
  208.   /* Stacker Anywhere returns pointer to 1 byte before the A55Ah
  209.      signature (which is at offset 1Ah), while all other versions
  210.      of Stacker point to the signature itself.  */
  211.   if (_farpeekw(dos_mem_base,   stac_driver_ptr) == 0xa55a ||
  212.       _farpeekw(dos_mem_base, ++stac_driver_ptr) == 0xa55a)
  213.     {
  214.       /* We have indeed Stacker device driver.  Get the volume
  215.          number (at offset 58h) and host drive (from the 26-byte
  216.          table beginning at offset 70h).  */
  217.       unsigned char seq  = _farpeekb(dos_mem_base, stac_driver_ptr + 0x3e);
  218.       unsigned char host = _farpeekb(dos_mem_base,
  219.                                      stac_driver_ptr + 0x55 + drive_num);
  220.  
  221.       sprintf(mnt_fsname, "%c:\\STACVOL.%03u", 'A' + host, seq);
  222.       mnt_type = NAME_stac;
  223.       return 1;
  224.     }
  225.   return 0;
  226. }
  227.  
  228. /*
  229.  * Get the network name which corresponds to a drive DRIVE_NUM.
  230.  * Ideally, _truename() (Int 21h/AH=60h) should return the same
  231.  * string, but some network redirectors don't put a full UNC
  232.  * name into the CDS, and others bypass the CDS altogether.
  233.  */
  234. static int
  235. get_netredir_entry(int drive_num)
  236. {
  237.   __dpmi_regs r;
  238.   unsigned long tb = __tb & 0xfffff;
  239.   unsigned char devname[2];
  240.   int i = -1;
  241.  
  242.   r.x.ds = r.x.es = (tb >> 4);
  243.   r.x.si = tb & 15;
  244.   r.x.di = r.x.si + 16;
  245.  
  246.   /* Loop for all the valid list indices, comparing the local device
  247.      name with our disk drive letter, until we find one which matches.
  248.  
  249.      We could cache entries which we've seen, but (1) I never said
  250.      this will be the fastest function ever; and (2) I can't be sure
  251.      the network configuration won't change between two successive
  252.      calls to getmntent().  */
  253.   while (++i < 35)  /* at the maximum: 32 drives + 3 LPTn devices */
  254.     {
  255.       r.x.cx = 0;
  256.       r.x.bx = i;
  257.       r.x.ax = 0x5f02;
  258.       __dpmi_int(0x21, &r);
  259.  
  260.       if (r.x.flags & 1)
  261.         {
  262.           if (r.x.ax == 0x12) /* end of list--bail out */
  263.             return 0;
  264.  
  265.           else
  266.             continue;
  267.         }
  268.       else if ((r.h.bh == 0 || r.h.bh == 2) && r.h.bl == 4)
  269.         {
  270.           /* We have a valid device which is a disk drive (BL = 4).
  271.              Pull in the local device name and if that fits our
  272.              drive number, get its network name.  */
  273.           *(unsigned short *)devname = _farpeekw(dos_mem_base, tb);
  274.  
  275.           /* The local device name may or may not include the
  276.              colon (Ralf Brown's Interrupt List).  */
  277.           if (devname[0] == '@' + drive_num &&
  278.               (devname[1] == ':' || devname[1] == '\0'))
  279.             {
  280.               movedata(dos_mem_base, tb + 16,
  281.                        our_mem_base, (unsigned)mnt_fsname, 128);
  282.               return 1; /* found! */
  283.             }
  284.         }
  285.     }
  286.   return 0;
  287. }
  288.  
  289. /*
  290.  * Return 1 if this drive is a CD-ROM drive, 0 otherwise.  Works
  291.  * with MSCDEX 2.x, but what about other CD-ROM device drivers?
  292.  */
  293. static int
  294. is_cdrom_drive(int drive_num)
  295. {
  296.   __dpmi_regs r;
  297.  
  298.   r.x.ax = 0x150b;      /* CD-ROM Drive Check function */
  299.   r.x.cx = drive_num - 1; /* 0 = A: */
  300.   __dpmi_int(0x2f, &r);
  301.  
  302.   /* If MSCDEX installed, BX will hold ADADh; AX will be non-zero
  303.      if this drive is supported by MSCDEX.  */
  304.   if (r.x.bx == 0xadad && r.x.ax != 0)
  305.     return 1;
  306.   return 0;
  307. }
  308.  
  309. /*
  310.  * Return 1 if a CD-ROM drive DRIVE_NUM is ready, i.e. there is a
  311.  * disk in the drive and that disk is a data (not AUDIO) disk.
  312.  */
  313. static int
  314. cdrom_drive_ready(int drive_num)
  315. {
  316.   __dpmi_regs r;
  317.   int i = 2;
  318.  
  319.   /* Int 2Fh/AX=1505h (Read Volume Table Of Contents) will return
  320.      with error for empty drives or if the disk is an AUDIO disk.  */
  321.   r.x.es = __tb;
  322.   r.x.bx = __tb & 15;
  323.   r.x.cx = drive_num - 1;   /* 0 = A: */
  324.   r.x.dx = 0;   /* get the 1st descriptor (usually, the only one) */
  325.  
  326.   /* First time after the door is closed the call might fail.
  327.      Therefore try twice before giving up.  */
  328.   do
  329.     {
  330.       r.x.ax = 0x1505;
  331.       __dpmi_int(0x2f, &r);
  332.     } while (--i && (r.x.flags & 1));
  333.  
  334.   if ((r.x.flags & 1) == 0 && (r.x.ax == 0 || r.x.ax == 1))
  335.     return 1;
  336.   return 0;
  337. }
  338.  
  339. /*
  340.  * Detect a RAM disk.  We do this by checking if the number of FAT
  341.  * copies (in the Device Parameter Block) is 1, which is typical of
  342.  * RAM disks.  [This doesn't _have_ to be so, but if it's good
  343.  * enough for Andrew Schulman et al (Undocumented DOS, 2nd edition),
  344.  * we can use this as well.]
  345.  */
  346. static int
  347. is_ram_drive(int drive_num)
  348. {
  349.   __dpmi_regs r;
  350.  
  351.   r.h.ah = 0x32;        /* Get Device Parameter Block function */
  352.   r.h.dl = drive_num;
  353.   __dpmi_int(0x21, &r);
  354.  
  355.   if (r.h.al == 0)
  356.     {
  357.       /* The pointer to DPB is in DS:DX.  The number of FAT copies is at
  358.          offset 8 in the DPB.  */
  359.       char fat_copies = _farpeekb(dos_mem_base, MK_FOFF(r.x.ds, r.x.dx) + 8);
  360.  
  361.       return fat_copies == 1;
  362.     }
  363.   return 0;
  364. }
  365.  
  366. /*
  367.  * Check if the media in this disk drive is fixed or removable.
  368.  * Should only be called after we're sure this ain't CD-ROM or
  369.  * RAM disk, since these might fool you with this call.
  370.  */
  371. static int
  372. media_type(int drive_num)
  373. {
  374.   __dpmi_regs r;
  375.  
  376.   r.x.ax = 0x4408;
  377.   r.h.bl = drive_num;
  378.   __dpmi_int(0x21, &r);
  379.  
  380.   if (r.x.flags & 1)
  381.     return -1;
  382.   return r.x.ax;    /* returns 1 for fixed disks, 0 for removable */
  383. }
  384.  
  385. /* Exported library functions.  */
  386.  
  387. FILE *
  388. setmntent(char *filename, char *type)
  389. {
  390.   __dpmi_regs r;
  391.   int cds_address_offset;
  392.   /* Need TRUE DOS version. */
  393.   unsigned short true_dos_version = _get_dos_version(1);
  394.  
  395.   dos_mem_base = _go32_info_block.selector_for_linear_memory;
  396.   our_mem_base = _my_ds();
  397.  
  398.   drive_number = 0;
  399.   skip_drive_b = 0;
  400.  
  401.   /* Get and save the pointer to the CDS array and the size of
  402.      the array element.  This is version-dependent.  */
  403.   if (true_dos_version < 0x0300 /* CDS not supported for v2.x */
  404.       || true_dos_version > 0x1000) /* dos emulation (OS/2) ? */
  405.     {
  406.       cds_elsize  = -1;
  407.       cds_address = 0;
  408.       cds_address_offset = 0; /* pacify -Wall */
  409.     }
  410.   else if (true_dos_version == 0x0300)
  411.     {
  412.       cds_address_offset = 0x17;
  413.       cds_elsize = 0x51;
  414.     }
  415.   else
  416.     {
  417.       cds_address_offset = 0x16;
  418.       cds_elsize = (true_dos_version >= 0x0400) ? 0x58 : 0x51;
  419.     }
  420.  
  421.   if (cds_elsize > 0)
  422.     {
  423.       unsigned long cds_address_ptr;
  424.       
  425.       r.h.ah = 0x52;    /* DOS Get List of Lists call */
  426.       __dpmi_int(0x21, &r);
  427.  
  428.       /* The pointer to the List of Lists is in ES:BX.  Compute the
  429.          linear address of the pointer to the CDS, which is at version-
  430.          dependent offset from the LoL start.  */
  431.       cds_address_ptr = MK_FOFF(r.x.es, r.x.bx + cds_address_offset);
  432.  
  433.       /* Get the pointer and compute the linear address of the CDS array.  */
  434.       cds_address = MK_FOFF(_farpeekw(dos_mem_base, cds_address_ptr + 2),
  435.                             _farpeekw(dos_mem_base, cds_address_ptr));
  436.     }
  437.  
  438.   return (FILE *) 1;
  439. }
  440.  
  441. static char NAME_unknown[] = "???";
  442. struct mntent *
  443. getmntent(FILE *filep)
  444. {
  445.   if (drive_number == -1)
  446.     return NULL;
  447.   if (filep != (FILE *)1)
  448.     {
  449.       errno = EBADF;    /* fake errno for invalid handle */
  450.       return NULL;
  451.     }
  452.  
  453.   /* The number of drives known to DOS is returned by the DOS
  454.      SetDisk (Int 21h/AH=0Eh) call.  Any drive beyond that
  455.      number can only come from some device driver which bypasses
  456.      the DOS network redirector interface and instead hooks Int 21h
  457.      directly (Novell NetWare 3.x is a good example).  Such drives
  458.      won't appear in the DOS Current Directory Structure (CDS).  */
  459.   cds_drives = setdisk(0xffff);
  460.  
  461.   /* There may be a maximum of 32 block devices.  Novell Netware indeed
  462.      allows for 32 disks (A-Z plus 6 more characters from [ to ' */
  463.   while (drive_number < 32)
  464.     {
  465.       unsigned char *p, *q;
  466.       char *truename_result;
  467.       struct ffblk   mnt_ff;
  468.       unsigned char  cds_path[128];
  469.       unsigned short cds_flags = 0;
  470.       int            drvstr_len;
  471.       int            got_fsname = 0;
  472.  
  473.       drive_number++;
  474.       mnt_dir[0] = '\0';
  475.  
  476.       /* On PCs with only one floppy drive, it can be mapped to either
  477.          A: or B:, depending on how it was last referenced.  We will
  478.          skip and not report the drive if it is actually mapped to a
  479.          physical drive which we already reported.  This method both
  480.          insures more accurate report and avoids the annoying message
  481.          ``Insert disk...'' from the OS when _truename() below hits
  482.          the disk.
  483.  
  484.          I assume there is only one such drive in the system, and that
  485.          it is drive A:, otherwise the logic below will fail miserably.  */
  486.       if (drive_number == 2 && skip_drive_b)
  487.         continue;
  488.  
  489.       /* Work around a possible bug in QDPMI: _truename() would sometimes
  490.          crash the calling program when there is no disk in a (floppy)
  491.          drive.  To avoid this, we have to check if the drive is empty
  492.          with a BIOS call.  */
  493.       if (drive_number <= 2)
  494.         {
  495.           unsigned char buf[512];
  496.           int bios_status, count = 0;
  497.  
  498.           /* Int 13h/AH=02h returns 6 for disk changed, even if the
  499.              disk isn't readable (e.g., unformatted).  Retry the
  500.              operation after disk change, each time resetting the 
  501.              drive, until something other than 6 is returned or we
  502.              run out of our patience.  */
  503.           while (++count < 10 && (bios_status =
  504.                  biosdisk(2, drive_number - 1, 0, 0, 1, 1, buf)) == 6)
  505.             biosdisk(0, drive_number - 1, 0, 0, 0, 0, NULL);
  506.  
  507.           /* If the loop ends with nonzero status, fail.  */
  508.           if (bios_status != 0)
  509.             continue;
  510.         }
  511.  
  512.       /* See whether drive A: is mapped to B: and if it is, change
  513.          DRIVE_NUMBER on the fly and raise the flag to skip drive
  514.          number 2 next time we are called (meaning there is only
  515.          one floppy drive).  */
  516.       if (drive_number == 1 && (drive_a_mapping = assigned_to(1)) > 0)
  517.         {
  518.           skip_drive_b = 1;
  519.           drive_number = drive_a_mapping;
  520.         }
  521.  
  522.       drvstr_len = sprintf(drive_string, "%c:\\", '@' + drive_number);
  523.       mnt_type = NAME_unknown;
  524.  
  525.       /* For the ``File System Name'' we use one of the following:
  526.  
  527.          * X:\DBLSPACE.NNN or X:\STACVOL.NNN for compressed drives,
  528.            where X: is the host drive of the compressed volume and NNN
  529.            is the volume sequence number;
  530.          * What _truename() returns for the root directory, in case
  531.            it isn't the usual ``X:\'';
  532.          * The name of the volume label;
  533.          * The string ``Drive X:'' (where X is the drive letter).
  534.  
  535.          These are used in the above order.  */
  536.  
  537.       /* See what 2160 can tell us.  If it fails, there ain't no such
  538.          drive, as far as DOS is concerned.  */
  539.       truename_result = _truename(drive_string, mnt_fsname);
  540.  
  541.       /* Get some info from the DOS Current Directory Structure (CDS).
  542.          We've already hit the disk with _truename(), so CDS now
  543.          contains valid and up to date data.  */
  544.       if (drive_number <= cds_drives)
  545.         cds_flags = get_cds_entry(drive_number, cds_path);
  546.  
  547.       if (truename_result != NULL)
  548.         {
  549.  
  550.           /* Determine the type of this drive, if not known already.
  551.  
  552.              We use one of the following types:
  553.  
  554.              fd     for floppy disks
  555.              hd     for hard disks
  556.              dblsp  for disks compressed with DoubleSpace
  557.              stac   for disks compressed with Stacker
  558.              cdrom  for CD-ROM drives
  559.              ram    for RAM disks
  560.              subst  for SUBSTed directories
  561.              join   for JOINed disks
  562.              net    for networked drives
  563.           */
  564.           if (mnt_type[0] == '?')
  565.             {
  566.               int disk_type = media_type(drive_number);
  567.  
  568.               if (is_ram_drive(drive_number))
  569.                 mnt_type = NAME_ram;
  570.               else if (is_cdrom_drive(drive_number))
  571.                 {
  572.                   /* Empty CD-ROM drives do NOT fail _truename(),
  573.                      so we must see if there is a disk in the drive.  */
  574.                   if (cdrom_drive_ready(drive_number))
  575.                     mnt_type = NAME_cdrom;
  576.                   else
  577.                     continue;           /* don't report this drive */
  578.                 }
  579.               /* _is_remote_drive() needs zero-based disk number  */
  580.               else if (_is_remote_drive(drive_number - 1) == 1)
  581.                 mnt_type = NAME_net;
  582.               else if (disk_type == REMOVABLE)
  583.                 mnt_type = NAME_fd;
  584.               else if (disk_type == FIXED)
  585.                 mnt_type = NAME_hd;
  586.             }
  587.  
  588.           /* If the canonicalized name isn't the boring ``X:\'' with X
  589.              the original drive letter, then it is either in the UNC
  590.              form (``\\MACHINE\PATH\'') or it's SUBSTed or JOINed drive,
  591.              and the canonicalized name might actually say something
  592.              interesting about this drive, so use it.  */
  593.           if (mnt_fsname[1] != ':')
  594.             got_fsname = 1;
  595.           else if (mnt_fsname[0] != drive_string[0])
  596.             {
  597.               /* Detect SUBSTed drives.  This could also be found by
  598.                  looking in the CDS, but we try to keep usage of
  599.                  undocumented features at the bare minimum.
  600.                  For SUBSTed drives, the root directory will be returned
  601.                  by _truename() as some directory on another drive.  */
  602.               mnt_type = NAME_subst;
  603.               got_fsname = 1;
  604.             }
  605.           else
  606.             {
  607.               /* Check for DoubleSpace- or Stacker- compressed drive.  */
  608.               if ((got_fsname = get_doublespace_info(drive_number)) == 0)
  609.                 got_fsname = get_stacker_info(drive_number);
  610.             }
  611.         }
  612.       /* JOINed drives fail _truename().  I don't know how to check
  613.          for them without using the CDS.  We will only trust the CDS
  614.          data if bits 14 and 15 aren't both zero, and if the current
  615.          directory path in the CDS isn't empty (this might happen
  616.          after ``JOIN D: /d'').  */
  617.       else if ((cds_flags & CDS_VALID) &&
  618.                (cds_flags & CDS_JOIN)  &&
  619.                cds_path[0] != '\0')
  620.         {
  621.           mnt_type = NAME_join;
  622.           strcpy(drive_string, cds_path);
  623.           drvstr_len = strlen(drive_string);
  624.           strcat(drive_string, "\\");
  625.  
  626.           /* Don't set got_fsname, so that we get a chance to look for
  627.              a volume label below.  */
  628.         }
  629.       else
  630.         continue;   /* illegal (non-present) drive */
  631.  
  632.       /* Some network redirectors don't set a UNC path to be
  633.          returned by _truename().  See if 215F02 can help.  */
  634.       if ((!got_fsname || mnt_fsname[0] != '\\') &&
  635.            strcmp(mnt_type, "net") == 0)
  636.         if (get_netredir_entry(drive_number))
  637.           got_fsname = 1;
  638.       if (!got_fsname || strcmp(mnt_type, "cdrom") == 0)
  639.         {
  640.           /* Look for the volume label.  */
  641.           int e = errno;
  642.  
  643.           strcat(drive_string, "*.*");
  644.           errno = 0;
  645.  
  646.           if (!findfirst(drive_string, &mnt_ff, FA_LABEL))
  647.             {
  648.               errno = e;
  649.  
  650.               /* Got label.  Strip out extraneous '.' separator, if present. */
  651.               strcpy(mnt_fsname, mnt_ff.ff_name);
  652.               if (strlen(mnt_fsname) > 8 && mnt_fsname[8] == '.')
  653.                 {
  654.                   /* Overlapping copy, don't use strcpy() */
  655.                   p = mnt_fsname + 8;
  656.                   q = p + 1;
  657.                   while ((*p++ = *q++) != '\0');
  658.                 }
  659.  
  660.               got_fsname = 1;
  661.             }
  662.  
  663.           else if (errno == ENMFILE || errno == ENOENT)
  664.             {
  665.               /* Valid drive, but no label.  Construct default
  666.                  filesystem name.  If drive A: is mapped to B:,
  667.                  call it ``Drive A:''.  */
  668.               (void) strcpy(mnt_fsname, "Drive  :");
  669.               mnt_fsname[6] = (drive_number == 2 && drive_a_mapping == 2)
  670.                               ? 'A'
  671.                               : '@' + drive_number;
  672.               got_fsname = 1;
  673.             }
  674.         }
  675.  
  676.       if (got_fsname)
  677.         {
  678.           char xdrive[3];
  679.  
  680.           /* Remove the '*.*' from the drive string for use as mnt_dir */
  681.           drive_string[drvstr_len] = '\0';
  682.           if (mnt_dir[0] == '\0')
  683.             strcpy(mnt_dir, drive_string);
  684.  
  685.           /* Format mnt_dir[] for beauty.  */
  686.           for (p = mnt_dir; *p; p++)
  687.             {
  688.               if (*p == '\\')
  689.                 *p = '/';
  690.               else
  691.                 *p = tolower(*p);
  692.             }
  693.  
  694.           /* Should we convert ``\\HOST\PATH'' into ``HOST:PATH''?  */
  695.           mntent.mnt_fsname = mnt_fsname;
  696.           mntent.mnt_dir    = mnt_dir;
  697.           mntent.mnt_type   = mnt_type;
  698.           mntent.mnt_opts   = dev_opts;
  699.  
  700.           /* Make CD-ROM drives read-only, others read-write.  */
  701.           if (strcmp(mnt_type, "cdrom") == 0)
  702.             dev_opts[1] = 'o';
  703.           else
  704.             dev_opts[1] = 'w';
  705.  
  706.           /* Include "dev=XX" in the mnt_opts field, where XX should
  707.              be consistent with what stat() returns in st_dev.  */
  708.           sprintf(xdrive, "%02x", strcmp(mnt_type, "subst")
  709.                                   ? drive_number - 1
  710.                                   : mnt_fsname[0] - 'A');
  711.           dev_opts[7] = xdrive[0];
  712.           dev_opts[8] = xdrive[1];
  713.           mntent.mnt_freq   = -1;
  714.           mntent.mnt_passno = -1;
  715.           mntent.mnt_time   = -1;
  716.  
  717.           return &mntent;
  718.         }
  719.  
  720.       /* Go try next drive, if any left.  */
  721.     }
  722.   
  723.   return NULL;
  724. }
  725.  
  726. int
  727. addmntent(FILE *filep, struct mntent *mnt)
  728. {
  729.   return 1;
  730. }
  731.  
  732. char *
  733. hasmntopt(struct mntent *mnt, char *opt)
  734. {
  735.   return strstr(mnt->mnt_opts, opt);
  736. }
  737.  
  738. int
  739. endmntent(FILE *filep)
  740. {
  741.   if (filep != (FILE *)1)
  742.     {
  743.       errno = EBADF;    /* fake errno for invalid handle */
  744.       return NULL;
  745.     }
  746.   drive_number = 0;
  747.   skip_drive_b = 0;
  748.   return 1;
  749. }
  750.  
  751. #ifdef  TEST
  752.  
  753. int main()
  754. {
  755.   FILE *mp = setmntent("", "r");
  756.   struct mntent *me;
  757.   int i = 0;
  758.  
  759.   if (!mp)
  760.     {
  761.       fprintf(stderr, "setmntent() failed\n");
  762.       return 1;
  763.     }
  764.  
  765.   while ((me = getmntent(mp)) != NULL)
  766.     printf("%d:  %s\t\t%s\t%s\t%s\n", i++,
  767.            me->mnt_fsname, me->mnt_type, me->mnt_opts, me->mnt_dir);
  768.   return 0;
  769. }
  770.  
  771. #endif
  772.